/**
 * 
 */
package com.gr.staffpm.pages.management.feedback;

import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.jxls.transformer.XLSTransformer;

import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.SecurityUtils;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.CloseButtonCallback;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.target.resource.ResourceStreamRequestTarget;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.PeriodType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.wicketstuff.annotation.mount.MountPath;
import org.wicketstuff.annotation.strategy.MountHybrid;
import org.wicketstuff.flot.Color;
import org.wicketstuff.flot.LegendPosition;

import com.gr.staffpm.StaffPMWebPage;
import com.gr.staffpm.charts.FlotPanel;
import com.gr.staffpm.charts.FlotUtils;
import com.gr.staffpm.charts.PredefinedCharts;
import com.gr.staffpm.charts.Series;
import com.gr.staffpm.datatypes.Feedback;
import com.gr.staffpm.datatypes.Group;
import com.gr.staffpm.datatypes.User;
import com.gr.staffpm.feedback.model.FeedbackCount;
import com.gr.staffpm.feedback.service.FeedbackService;
import com.gr.staffpm.security.constants.StaffPMRoles;
import com.gr.staffpm.security.service.UserService;
import com.gr.staffpm.tasks.model.UserChoiceRenderer;
import com.gr.staffpm.utils.ByteArrayStreamResourceWriter;
import com.gr.staffpm.utils.ListUtils;
import com.gr.staffpm.widget.date.DatePicker;
import com.gr.staffpm.widget.dialog.feedback.AjaxFeedbackDirectDialog;


/**
 * @author Graham Rhodes 6 Apr 2011 17:12:33
 */
@MountHybrid
@MountPath(path = "secure/Feedback")
public class TeamFeedbackPage extends StaffPMWebPage {

    @SpringBean(name = "feedbackService")
    private FeedbackService feedbackService;
    @SpringBean(name = "userService")
    private UserService userService;
    private final Logger log = LoggerFactory.getLogger(getClass());
    private final FlotPanel panel;
    private final AjaxFeedbackDirectDialog feedbackDialog;
    private DropDownChoice<User> members;
    private FeedbackPanel errorPanel;
    private List<User> team = new ArrayList<User>();
    private DatePicker startPicker, endPicker;

    public TeamFeedbackPage() {

        User currUser = userService.getCurrentUser();

        add(errorPanel = new FeedbackPanel("feedback"));
        errorPanel.setOutputMarkupId(true);
        final WebMarkupContainer content = new WebMarkupContainer("content");
        content.setOutputMarkupId(true);

        content.add(new AjaxLink<Void>("addFeedbackLink") {

            private static final long serialVersionUID = 1L;

            @Override
            public void onClick(AjaxRequestTarget target) {
                launchLeaveFeedbackDialog(target);
            }

        }.setVisible(SecurityUtils.getSubject().hasRole(StaffPMRoles.MANAGER)));
        Form<Void> form = new Form<Void>("form");

        Set<Group> groups = currUser.getGroups();
        for (Group group : groups)
            team.addAll(userService.getUsersInGroupByGroupName(group.getName(), currUser, userService.getUsersHighestLevel(currUser)));

        team = ListUtils.removeDuplicates(team);
        Collections.sort(team);

        if (SecurityUtils.getSubject().hasRole(StaffPMRoles.MANAGER) && !team.isEmpty()) {
            members = new DropDownChoice<User>("members", new Model<User>(team.get(team.indexOf(currUser))), team, new UserChoiceRenderer());
        } else
            members = new DropDownChoice<User>("members", new Model<User>(currUser), Arrays.asList(currUser), new UserChoiceRenderer());

        form.add(members);

        startPicker = new DatePicker("start", new DateTime().minusDays(30));
        endPicker = new DatePicker("end");
        form.add(startPicker);
        form.add(endPicker);

        form.add(new AjaxButton("submitButton") {

            private static final long serialVersionUID = 1L;

            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                TeamFeedbackPage.this.optionSelected(target);
            }
        }.setDefaultFormProcessing(false));

        content.add(form);

        panel = new FlotPanel("chart", 1000, 500, new PropertyModel<List<Series>>(this, "data"));
        panel.setOutputMarkupId(true);
        panel.setLegendPosition(LegendPosition.NORTHWEST);
        panel.setLegendColumns(2);
        DateTime start = new DateTime(startPicker.getDefaultModelObject()).withTime(0, 0, 0, 0);
        DateTime end = new DateTime(endPicker.getDefaultModelObject()).withTime(start.hourOfDay().getMaximumValue(), 59, 59, 59);
        Period p = new Period(start, end, PeriodType.weeks());
        panel.setAxisTicksX(FlotUtils.getTicksForWeeksBetween(start, p.getWeeks()));
        panel.setAxisTimeModeX(true);
        panel.setAxisMinX(start);
        panel.setAxisMaxX(end);
        panel.setAxisMinY(0.0);
        panel.setAxisMinTimeTickSizeX(1, "week");
        panel.setPointsVisible(true);
        content.add(panel);

        content.add(new Link<Void>("export") {

            private static final long serialVersionUID = 1L;

            @Override
            public void onClick() {
                exportToExcel();
            }

        });
        add(content);

        List<User> teamMembers = new ArrayList<User>(team);
        if (team.contains(currUser))
            teamMembers.remove(currUser);
        feedbackDialog = new AjaxFeedbackDirectDialog("feedbackDialog", teamMembers);
        feedbackDialog.setCloseButtonCallback(new CloseButtonCallback() {

            private static final long serialVersionUID = 1L;

            @Override
            public boolean onCloseButtonClicked(AjaxRequestTarget target) {
                optionSelected(target);
                target.addComponent(content);
                return true;
            }
        });
        addOrReplace(feedbackDialog);
    }

    protected void launchLeaveFeedbackDialog(AjaxRequestTarget target) {
        if (!team.isEmpty())
            feedbackDialog.show(target);
    }

    public List<Series> getData() {
        DateTime start = new DateTime(startPicker.getDate()).withTime(0, 0, 0, 0);
        DateTime end = new DateTime(endPicker.getDate()).withTime(23, 59, 59, 59);
        log.debug("Get feedback data between " + start.toString("dd/MM/yyyy") + " and " + end.toString("dd/MM/yyyy"));
        List<List<Feedback>> stars = new ArrayList<List<Feedback>>();
        List<Color> colors = new ArrayList<Color>();
        int days = 0;
        colors.add(new Color(0xE6, 0x16, 0x16));
        colors.add(new Color(0xFF, 0x66, 0x00));
        colors.add(new Color(0x00, 0xCC, 0xFF));
        colors.add(new Color(0x00, 0x00, 0xFF));
        colors.add(new Color(0xFF, 0xCC, 0x00));
        colors.add(new Color(0x00, 0x99, 0x00));
        while (start.isBefore(end)) {
            List<Feedback> feedbackForDay = new ArrayList<Feedback>();
            for (int i = 0; i < 6; i++) {
                //For each star
                feedbackForDay.addAll(feedbackService.getFeedbackForUserBetweenDatesWithRating(members.getModelObject(), start,
                        start.withTime(start.hourOfDay().getMaximumValue(), 59, 59, 59), i));
            }
            stars.add(feedbackForDay);
            start = start.plusDays(1);
            days++;
        }
        return PredefinedCharts.feedback(start, stars, colors, days);
    }

    protected void exportToExcel() {
        try {
            Map<String, Object> beans = new HashMap<String, Object>();
            beans.put("user", members.getModelObject());
            SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy");
            beans.put("dateFormat", dateFormat);
            DateTime start = new DateTime(startPicker.getDate()).withTime(0, 0, 0, 0), end = new DateTime(endPicker.getDate()).withTime(start.hourOfDay().getMaximumValue(), 59, 59, 59);
            beans.put("start", start.toDate());
            beans.put("end", end.toDate());

            List<Feedback> feedbackForDay = new ArrayList<Feedback>();
            List<FeedbackCount> feedbackCountForDay = new ArrayList<FeedbackCount>();
            feedbackForDay.addAll(feedbackService.getFeedbackForUserBetweenDates(members.getModelObject(), start, end));
            Collections.sort(feedbackForDay);

            beans.put("feedback", feedbackForDay);

            while (!start.isAfter(end)) {
                FeedbackCount count = new FeedbackCount();
                for (int i = 0; i < 6; i++) {
                    int result = feedbackService.countRatingForDay(members.getModelObject(), start, i);
                    count.set(result);
                }
                count.setDate(start.toDate());
                feedbackCountForDay.add(count);
                start = start.plusDays(1);
            }
            beans.put("feedbackCounts", feedbackCountForDay);
            beans.put("counts", feedbackCountForDay.size());

            XLSTransformer transformer = new XLSTransformer();
            Workbook work = transformer.transformXLS(new ClassPathResource("templates/excel/feedback_template.xls").getInputStream(), beans);
            final ByteArrayOutputStream out = new ByteArrayOutputStream();
            work.write(out);

            DateTime startDate = new DateTime(this.startPicker.getDate()), endDate = new DateTime(this.endPicker.getDate());
            String filename = members.getModelObject().getUsername() + "_" + startDate.toString("ddMMMyy") + "_" + endDate.toString("ddMMMyy") + "_feedback.xls";
            getRequestCycle().setRequestTarget(new ResourceStreamRequestTarget(new ByteArrayStreamResourceWriter(out, "application/vnd.ms-excel"), filename));
            out.close();
        } catch (Exception e) {
            log.error("Exception exporting feedback to excel.", e);
        }
    }

    protected void optionSelected(AjaxRequestTarget target) {
        DateTime start = new DateTime(startPicker.getDate()).withTime(0, 0, 0, 0);
        DateTime end = new DateTime(endPicker.getDate()).withTime(start.hourOfDay().getMaximumValue(), 59, 59, 59);
        log.debug("Get feedback between " + start.toString("dd/MM/yyyy") + " and " + end.toString("dd/MM/yyyy"));
        if (start.isBefore(end)) {
            Period p = new Period(start, end, PeriodType.weeks());
            if (p.getWeeks() >= 1) {
                panel.setDefaultModel(new PropertyModel<List<Series>>(this, "data"));
                panel.setAxisTicksX(FlotUtils.getTicksForWeeksBetween(start, p.getWeeks()));
                panel.setAxisMinX(start);
                panel.setAxisMaxX(end);
                target.addComponent(panel);
            } else
                error("Date range must be at least a week.");
            target.addComponent(errorPanel);
        } else {
            error("The start date must be before the end date.");
            target.addComponent(errorPanel);
        }
    }

    @Override
    public String getTitle() {
        return "Team Feedback - StaffPM";
    }

}
